﻿using System;
using System.Buffers;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Net.Sockets.Kcp;
using System.Threading.Tasks;
using UnityEngine.UIElements;
using static System.Net.Sockets.Kcp.SimpleSegManager;

namespace GameLogic
{
    public class UDPClientHandle : ClientHandle, IKcpCallback
    {
        public Kcp Kcp { get => m_Kcp; }
        public IPEndPoint EndPoint { get => m_EndPoint; set => m_EndPoint = value; }

        Kcp m_Kcp;
        UdpClient m_UDPClient;
        IPEndPoint m_EndPoint;

        UDPClientHandle()
        {

        }
        public static UDPClientHandle Create(UdpClient clicent, IPEndPoint endPoint, uint conv = 1124)
        {
            UDPClientHandle client = new UDPClientHandle();
            client.Init(clicent, endPoint, conv);
            return client;
        }
        void Init(UdpClient clicent, IPEndPoint endPoint, uint conv = 1124)
        {
            m_Kcp = new SimpleSegManager.Kcp(conv, this);
            m_EndPoint = endPoint;
            m_UDPClient = clicent;
            m_Kcp.NoDelay(1, 10, 2, 1);//fast

            KcpUpdate();
            TryReceive();
        }
        public override void Send(byte[] data, int length)
        {
            m_Kcp.Send(data, length);
        }
        public void Output(IMemoryOwner<byte> buffer, int avalidLength)
        {
            var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
            m_UDPClient.SendAsync(s, s.Length, m_EndPoint);
            buffer.Dispose();
        }
        public void Update(DateTimeOffset data)
        {
            m_Kcp.Update(data);
        }
        public void Input(byte[] data)
        {
            m_Kcp.Input(data);
        }
        public override void Close()
        {
            m_Kcp.Dispose();
            m_Kcp = null;
        }
         async void KcpUpdate()
        {
            while(true)
            {
                if (m_Kcp == null)
                    break;
                try
                {
                    if(HasHandShake)
                    {
                        await Task.Run(() =>
                        {
                            Update(DateTimeOffset.UtcNow);
                        });
                    }
                }
                catch (Exception e)
                {
                    UnityEngine.Debug.LogError(e.ToString());
                }
                await Task.Delay(10);
            }
        }
        async void TryReceive()
        {
            while(true)
            {
                if (m_Kcp == null)
                    break;
                await Task.Run(() =>
                {
                    int len;
                    while ((len = Kcp.PeekSize()) > 0)
                    {
                        var buffer = new byte[len];
                        if (Kcp.Recv(buffer) >= 0)
                        {
                            try
                            {
                                m_ReceiveCallback.Invoke(EndPoint, buffer, 0, len);
                            }
                            catch (Exception e)
                            {
                                UnityEngine.Debug.LogError(e.ToString());
                            }
                        }
                    }
                });
            }
        }
    }
}
